package com.api.signature.auto.util;

import com.api.signature.auto.config.ApiSignConfig;
import com.api.signature.auto.spring.ApiSignContext;
import com.signature.auto.common.exception.ApiSignException;
import com.signature.auto.common.util.ApiFoxUtil;
import com.signature.auto.common.util.SecureUtil;
import com.signature.auto.common.util.SignUtil;

import java.util.Map;
import java.util.Optional;

/**
 * @author Jerry
 * @date 2024-01-31 17:36
 */
public class ServerSignUtil extends SignUtil {


    /**
     * 加密方式
     * @param fullStr 待摘要的字符串
     * @return
     */
    @Override
    public String abstractStr(String fullStr) {
        String encryption = ApiSignContext.apiSignatureConfig.getEncryption();
        if (encryption.equals("MD5")){
            return SecureUtil.md5(fullStr);
        }
        // 默认加密方式
        return SecureUtil.md5(fullStr);
    }

    /**
     * 给 paramsMap 追加 timestamp、nonce、sign 三个参数
     *
     * @param paramsMap 参数列表
     * @return 加工后的参数列表
     */
    @Override
    public Map<String, Object> addSignParams(Map<String, Object> paramsMap, String secretKey) {
        paramsMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
        paramsMap.put("nonce", ApiFoxUtil.getRandomString(32));
        paramsMap.put("sign", createSign(paramsMap, secretKey));
        return paramsMap;
    }

    /**
     * 判断：指定时间戳与当前时间戳的差距是否在允许的范围内
     *
     * @param timestamp 待校验的时间戳
     * @return 是否在允许的范围内
     */
    @Override
    public boolean isValidTimestamp(long timestamp) {
        long allowDisparity = getSignConfigOrGlobal().getTimestampDisparity();
        long disparity = Math.abs(System.currentTimeMillis() - timestamp);
        return allowDisparity == -1 || disparity <= allowDisparity;
    }

    /**
     * 判断：参数列表中的 nonce、timestamp、sign 是否均为合法的
     *
     * @param paramMap 待校验的请求参数集合
     * @return 是否合法
     */
    @SuppressWarnings("all")
    @Override
    public boolean isValidParamMap(Map<String, Object> paramMap, String secretKey) {
        // 获取必须的三个参数
        String timestampValue = paramMap.get("timestamp").toString();
        String nonceValue = String.valueOf(paramMap.get("nonce"));
        String signValue = String.valueOf(paramMap.get("sign"));

        // 参数非空校验
        ApiSignException.throwByNull(timestampValue, "缺少 timestamp 字段");
        // ApiSignException.throwByNull(nonceValue, "缺少 nonce 字段"); // 配置isCheckNonce=false时，可以不传 nonce
        ApiSignException.throwByNull(signValue, "缺少 sign 字段");

        // 三个值的校验必须全部通过
        return isValidTimestamp(Long.parseLong(timestampValue))
                && (getSignConfigOrGlobal().getIsCheckNonce() ? isValidNonce(nonceValue) : true)
                && isValidSign(paramMap, signValue, secretKey);
    }

    /**
     * 校验：参数列表中的 nonce、timestamp、sign，appId 是否均为合法的，如果不合法，则抛出对应的异常
     *
     * @param paramMap 待校验的请求参数集合
     */
    @Override
    public void checkParamMap(Map<String, Object> paramMap, String secretKey) {
        // 获取必须的四个参数
        Object timestampValue = paramMap.get("timestamp");
        Object nonceValue = paramMap.get("nonce");
        Object signValue = paramMap.get("sign");
        Object appId = paramMap.get("appId");
        // 参数非空校验
        ApiSignException.throwByNull(timestampValue, "缺少 timestamp 字段");
        // SaSignException.throwByNull(nonceValue, "缺少 nonce 字段"); // 配置isCheckNonce=false时，可以不传 nonce
        ApiSignException.throwByNull(signValue, "缺少 sign 字段");
        ApiSignException.throwByNull(appId, "缺少 appId 字段");
        // 依次校验三个参数, 注意：appId为渠道唯一标识，应该由数据库控制
        checkTimestamp(Long.parseLong(String.valueOf(timestampValue)));
        if (getSignConfigOrGlobal().getIsCheckNonce()) {
            checkNonce(String.valueOf(nonceValue));
        }
        checkSign(paramMap, String.valueOf(signValue), secretKey);
    }


    /**
     * 获取：API 签名配置：
     * 1. 如果用户自定义了 signConfig ，则使用用户自定义的。
     * 2. 否则使用全局默认配置。
     *
     * @return /
     */
    public ApiSignConfig getSignConfigOrGlobal() {
        // 全局默认配置
        return Optional.ofNullable(ApiSignContext.getConfig()).orElseThrow(
                () -> new ApiSignException("Api-Signature 未开启配置"));
    }
}
